"""TimeData Indicators."""

import datetime as dt
import numpy as np
import misc.quoteperiod as qp
import system.navalasystem as na
from misc import parts
from misc import timedata
from misc import misc
from misc import timedata
from indicator import Indicator
from block import block
import maths


class TimeDataIndicator(Indicator, block.IOBlock):
    """Calculates an output TimeData given an existing TimeData."""
    
    def __init__(self, *args, **kwargs):
        Indicator.__init__(self, *args, **kwargs)
        block.IOBlock.__init__(self, *args, **kwargs)
        
    def _Use(self):
        r = self._GetOutputTD()
        if r is None:
            raise RuntimeError("_GetOutputTD() returned None!")
        self["output"][0] = r

    ## Tools for use by descendant classes

    def _GetNewLaggedTD(self, lag, newLen=None):
        """Creates new TimeData that is smaller than the input.
        
        Arguments:
            lag -- Integer and > 0 specifying number of time shifts into the
                   future.
            newLen -- Size of new TimeData
        
        timestamp comes sized and filled. Uses self["input"][0] to get
        timestamp[lag:]
        
        If newLen is not passed, it will be automatically calculated as the
        original size minus lag.
        
        If the new timestamp vector extends into the future beyond the existing
        timestamp vector, it will be made up somehow
        ."""
        inputTD = self["input"][0]
        return inputTD.LaggedFlatCopy(lag, newLen)
##########################        len0 = len(inputTD)
##########################        if newLen is None:
##########################            newLen = max(len0-lag, 0)
##########################        r = self._TDClass() # the class should be a timedata.Generic
##########################        if newLen > 0:
##########################            r.SetSize(newLen)
##########################        if lag < len0:
##########################            idx1, idx2 = lag, min(lag+newLen, len0)
##########################            t = np.copy(inputTD.timestamp[idx1:idx2])
##########################            r.timestamp[0:len(t)] = t
##########################        if newLen+lag > len0:
##########################            dif = newLen-lag-len0
##########################            idxs = np.arange(max(len0, lag), newLen+lag) # Indexes to be used as interpolation input
##########################            t1 = inputTD.timestamp[-1]
##########################            t0 = inputTD.timestamp[0]
##########################            r.timestamp[-dif:] = t1+(t1-t0)/len0*(idxs-len0)
##########################        return r
        
    def _GetInputTD(self):
        """Just a shortcut to self["input"][0], looking nicer."""
        return self["input"][0]
        
    def _GetInputY(self):
        """Just a shortcut to self["input"].GetY(), looking nicer."""
        return self["input"].GetY()
        
        
        

    # Must be reimplemented to perform the calculation
    def _GetOutputTD(self):
        """Returns output TimeData"""
        raise NotImplementedError("Not implemented")
        

###
###class SeriesIndicator(TimeDataIndicator):
###    """Indicator that uses a single time series vector."""
###    
###    def GetXYN(self):
###        """Returns x, y, n. This is a commonly used shortcut.
###        
###        x is the timestamp vector of the input Terminal
###        y is the y-vector of the input, obtained using Terminal.GetY()
###        n is len(x) or len(y)
###        """
###        x = self["input"][0]
###        y = self["input"].GetY()
###        n = len(x)
###        return (x, y, n)
###        

class FIRFilter(TimeDataIndicator):
    """Finite Impulse Response Filter.

    TODO: NOT TESTED!!!!
    Output is GetY() convolved with ir
    """
    # =np.array([1]) (identity). Impulse Response (causal).
    ir = np.array([1])

    def _GetOutputTD(self):
        lenIR = len(self.ir)
        r = self._GetNewLaggedTD(lenIR-1)
        r.y = np.convolve(self.GetInputY(), self.ir, 'valid')
        return r


class MA(FIRFilter):
    """Moving Average."""
    ## Setup
    @property
    def size(self):
        return len(self.ir)
    @size.setter
    def size(self, x):
        self.ir = np.ones([x])/x

class EMA(TimeDataIndicator):
    """Exponential Moving Average.
    
    For formula, see maths.EMA()
    """
    ## Setup
    # =13. "Size" parameter.
    size = 13
    
    def _GetOutputTD(self):
        size = self.size
        r = self._GetNewLaggedTD(size-1)
        r.y = maths.EMA(self._GetInputY(), size)
        return r


class Stochastic(TimeDataIndicator):
    """Stochastic Oscillator.
    
    For formula, see maths.Stochastic()
    """
    ## Setup
    # =9. Parameter of Stochastic oscillator.
    size = 9
    
    def _GetOutputTD(self):
        size = self.size
        td = self._GetInputTD()
        r = self._GetNewLaggedTD(size-1)
        r.y = maths.Stochastic(td.close, td.high, td.low, size)
        return r



class LSPredictor(TimeDataIndicator):
    # Size of training signal
    trainingSize = 20
    # Filter order (design matrix will have order+1 columns)
    order = 6
    # "time travel". 0 will predict current time
    travel = 1


    def _GetOutputTD(self):
        td = self._GetInputTD().GetGeneric("close", False) # TODO: this is obviously ridiculous, I need to create the output TimeData of the right class, implement a method somewhere, maybe a static method of TimeDataIndicator
        r = td.LaggedFlatCopy(self.trainingSize-1+self.travel, len(td)-self.trainingSize+1)
###        print "------------------------------------------"
###        print "-1-", len(td)
###        print "-2-", len(r)
        r.y = maths.LSPredictions(td.y, self.trainingSize, self.order, self.travel)
        return r



####class EMAGemeu(TimeDataIndicator):
####    """Testing Online EMA...........................................................    """
####
####    
####    @property
####    def size(self):
####        """Size property. See EMA formula for more information."""
####        return self.__size
####    
####    @size.setter
####    def size(self, x):
####        self.__size = x
####    
####    def __UpdateSize(self):
####        self.__k = 2./(self.__size+1)
####        self.__first.maxLen = self.__size
####    
####    def __init__(self, *args, **kwargs):
####        TimeDataIndicator.__init__(self, *args, **kwargs)
####        
####        # =13. Number of data points for moving average
####        self.__size = 13
####        # k constant is calculated automatically when size is set
####        self.__k = 0
####        # First self.size data points to calculate initial value
####        self.__first = timedata.Generic()
####        # Last y-value used in the recursive formula
####        self.__lastY = None
####        # Whether has reached the point when the EMA can actually be calculated 
####        self.__flagLastY = False
####
####        self.__UpdateSize() # Updates __k and __first.maxLen
####
####    
####    def _Use(self):
####        td = self["input"].GetGeneric(0)[-1]
####        r = None
####        if self.__flagLastY:
####            r = timedata.Generic()
####            r.SetSize(1)
####            r.timestamp[0] = td.timestamp[0]
####            r.y = (1-self.__k)*self.__lastY + self.__k*td.y[0]
####            self.__lastY = r.y
####        else:
####            self.__first.Append(td)
####            if len(self.__first) == self.__size:
####                # When number of stored data points reaches self.size, calculates standard moving average
####                r = timedata.Generic()
####                r.SetSize(1)
####                r.timestamp[0] = td.timestamp[0]
####                r.y[0] = np.mean(self.__first.y)
####                self.__flagLastY = True
####                self.__lastY = r.y


#class Feeder(block.OBlock):
#    pass




class Momentum(TimeDataIndicator):
    """"Momentum" indicator.
    
    For formula, see maths.Momentum()
    """
    ## Setup
    # =1. 
    size = 1
    
    def _GetOutputTD(self):
        y = self._GetInputY()
        r = self._GetNewLaggedTD(self.size)
        r.y = maths.Momentum(y, self.size)
        return r

